
window.track = {
    init(){
        this.clickArr = []
        this.allAddEventListeners = []
        this.trackClick()
        this.trackPageStay()
        this.trackXML()
        this.trackPerformance()
        this.customSendUrl = null
    },
    end(val){
        const endTime = new Date().getTime()
        this.clickArr = this.clickArr.filter(item => item)
        for (let i = 0; i < this.clickArr.length; i++) {
            const item = this.clickArr[i]
            if(item){
                const {trackTime} = item
                // 判断计算距离时间最近的那个时间,相差小于20毫秒就是我们要的那个对象
                if(Math.abs(trackTime - endTime) < 20){
                   this.clickArr[i].clearTime()
                    console.log(trackTime-endTime,Math.abs(trackTime - endTime) < 20,'Math.abs(trackTime - endTime) < 20')
                   const clickObj = JSON.parse(JSON.stringify(this.clickArr[i]))
                   this.clickArr[i] = null
                   this.send('clickToRequest_time',{ describe:endTime-clickObj.trackBeginTime, remark:val })
                }
            }
        }
    },
    // 监听按钮点击事件
    trackClick(){
        const global = this
        // 重写事件监听,获取所有注册了事件监听的dom
        const oldAddEventListener = HTMLElement.prototype.addEventListener
        HTMLElement.prototype.addEventListener = function(){
            if(arguments[0] === 'click'){
                this.localName !== 'html' ? global.allAddEventListeners.push(this) : ''
            }
            oldAddEventListener.apply(this,arguments)
        }
        const oldRemoveEventListener= HTMLElement.prototype.removeEventListener
        HTMLElement.prototype.removeEventListener = function(){
            if(arguments[0] === 'click'){
                const oldArr = global.allAddEventListeners
                const otherArr = global.allAddEventListeners.filter(item => item !== this)
                if(oldArr.length > otherArr.length){
                    global.allAddEventListeners = otherArr
                }
            }
            oldRemoveEventListener.apply(this,arguments)
        }
        const handel = (e) =>{
            const clickBox = e.target
            const currentTime = new Date().getTime()
            // 那就要判断点击是注册了点击事件的
            // if(clickBox.onclick || ['BUTTON'].includes(clickBox.tagName) || clickBox.parentNode.tagName === 'BUTTON'){
            if(clickBox.onclick || ['BUTTON'].includes(clickBox.tagName) || clickBox.parentNode.tagName === 'BUTTON' || global.allAddEventListeners.includes(clickBox) || global.allAddEventListeners.includes(clickBox.parentNode)){
                // console.log(clickBox,'clickBox')
                let clickObj = {}
                if(['BUTTON'].includes(clickBox.tagName) || clickBox.parentNode.tagName === 'BUTTON'){
                    clickObj = { describe:`${clickBox.innerText}-按钮`,currentTime }
                }else{
                    clickObj = { describe:`${clickBox.innerText}`,currentTime }
                }
                // 判断有没trackBegin属性,有就添加一个包含点击对象和实时在计算着时间的计时器的对象到clickArr这个数组里,为后来点击事件到接口完成做铺垫
                if(clickBox.getAttribute('trackBegin') || clickBox.getAttribute('trackBegin') === ''){
                    const needObj = {
                        trackBeginTime:currentTime,
                        clickObj,
                        timeID:null,
                        trackTime:null,
                        starTime:function(){
                            this.timeID = window.setInterval(() =>{
                                this.trackTime = new Date().getTime()
                            }, 1)
                        },
                        clearTime:function(){
                            window.clearInterval(this.timeID)
                        }
                    }
                    needObj.starTime()
                    global.clickArr.push(needObj)
                }
                // 上报点击事件
                this.send('clickEvent',clickObj)
            }
        }
        window.addEventListener('click',handel,true)
    },
    // 监听页面的停留时间
    trackPageStay(){
        let pageBeginTime = new Date().getTime()
        let beforeTitle = document.title
        let beforePageUrl = location.href
        // 页面停留时间上报,停留1秒以上的,小于1秒的很多都是用来重定向的页面,上报来没意义
        const trackPageStaySend = () =>{
            const pageEndTime = new Date().getTime()
            const stayTime = pageEndTime - pageBeginTime
            const obj = {
                describe:stayTime,
                pageTitle:beforeTitle,
                pageUrl:beforePageUrl
            }
            stayTime > 1000 ? this.send('pageStayTime',obj) : ''  // 箭头函数this指向的是对象,普通函数this指向的是window
            pageBeginTime = new Date().getTime()
            beforeTitle = document.title
            beforePageUrl = location.href
        }
        // 页面销毁阶段,页面点击刷新按钮的时候会触发,一点刷新就上报
        window.addEventListener('beforeunload',()=>{
            trackPageStaySend()
        })
        const oldPushState = window.history.pushState
        window.history.pushState = function(){
            oldPushState.apply(this,arguments)
            trackPageStaySend()
        }
        const oldReplaceState = history.replaceState
        window.history.replaceState = function(){
            oldReplaceState.apply(this,arguments)
            trackPageStaySend()
        }
        // back、go和forward触发这个事件
        window.addEventListener('popstate',()=>{
            trackPageStaySend()
        })
    },
    // 监听请求,二次封装请求,XML和fetch
    trackXML(){
        const global = this
        const XMLHttpRequest = window.XMLHttpRequest
        const oldOpen = XMLHttpRequest.prototype.open
        // 发送XHL请求,直接修改它的原型
        XMLHttpRequest.prototype.open = function(){
            const requestBeginTime = new Date().getTime()
             let elseObj = {}
             this.addEventListener('load',()=>{
                elseObj = {status:this.status,method:arguments[0],url:this.responseURL}
                loadQuest(requestBeginTime,elseObj)
             })
             this.addEventListener('error',()=>{
                elseObj = {status:this.status,method:arguments[0],url:this.responseURL}
                loadQuest(requestBeginTime,elseObj)
             })
             // 触发原生事件,arguments是请求参数数组,['POST', '/api/cfk/payCenter/permission/getManagerCompanyIdList', true}
             oldOpen.apply(this,arguments)
        }
        // 发送fetch请求,Promise二次封装
        const oldFetch = window.fetch
        window.fetch = (url,options) =>{
            const requestBeginTime = new Date().getTime()
            const elseObj = {method: !options || !options.method ? 'GET' : 'POST'}
            return new Promise((resolve,reject)=>{
                oldFetch(url,options).then((res)=>{
                    resolve(res)
                    elseObj.status = res.status
                    elseObj.url = res.url
                    loadQuest(requestBeginTime,elseObj)
                }).catch((err)=>{
                    reject(err)
                    elseObj.status = err.status
                    elseObj.url = err.url
                    loadQuest(requestBeginTime,elseObj)
                })
            })
        }
        // 请求成功完成的逻辑
        function loadQuest(requestBeginTime,elseObj){
            const requestEndTime = new Date().getTime()  // 请求完成时间
            global.send('requestTime',{ describe: requestEndTime-requestBeginTime,...elseObj })
        }
    },
    // 监听性能
    trackPerformance(){
        window.onload = () =>{
          // console.log(performance.getEntries(),'performance.getEntries')
          const entries = performance.getEntries()
          for (const item of entries) {
              if(item.entryType === 'navigation'){
                const obj = {
                    name:item.name,
                    type:item.entryType,
                    DNS:item.domainLookupEnd - item.domainLookupStart,  // DNS查询耗时
                    TCP:item.connectEnd - item.connectStart,  // TCP链接耗时
                    SSL:item.connectEnd - item.secureConnectionStart,  // SSL安全连接耗时
                    request:item.responseEnd - item.responseStart,  // 请求耗时
                    domTree:item.domComplete - item.domInteractive,  // 解析dom树耗时
                    firstRender:item.responseStart - item.startTime,  // 首次渲染时间/白屏时间
                    domready:item.domContentLoadedEventEnd - item.startTime,  // domready时间
                    duration:item.duration  // onload时间(总下载时间)
                }
                this.send('navigationTime',obj)
              }else if(item.entryType === 'resource'){
                const obj = {
                    name:item.name,
                    type:item.entryType,
                    duration:item.duration,  // 整个过程时间
                    DNS:item.domainLookupEnd - item.domainLookupStart,  // DNS查询时间
                    TCP:item.connectEnd - item.connectStart,  // TCP三次握手时间(HTTP)
                    SSL:item.connectEnd - item.secureConnectionStart,  //  SSL握手时间(HTTPS协议会有SSL握手)
                    TTFB:item.responseStart - item.startTime,  // TTFB(首包时间)
                    response:item.responseEnd - item.responseStart  // 响应时间(剩余包时间)
                }
                this.send('resourceTime',obj)
              }
          }
        }
    },
    // 上报
    send(type,val){
        val.type = type
        !val.currentTime ? val.currentTime = new Date().getTime() : ''
        !val.pageTitle ? val.pageTitle = document.title : ''
        !val.pageUrl ? val.pageUrl = location.href : ''
        const data = new URLSearchParams(val).toString()
        console.log(`上报类别:${val.type}`,val)
        if(!this.customSendUrl){
            // console.log(`上报类别:${val.type}`,val)
        }else{
            new Image().src = `${this.customSendUrl}?${data}`
            // new Image().src = `http://localhost:8080/getSendData?${data}`
        }
    },
    // DOM去重
    singleArray(array) {
        const newArr = []
        for (let i=0; i<array.reverse().length; i++) {
            if (!~newArr.indexOf(array[i])) {
                newArr.push(array[i])
            }
        }
        return newArr
    }
}